
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <SFML/Window/Mouse.hpp>
#include <SFML/Window/Keyboard.hpp>
#include "types.h"

#include "VertexBufferObject.h"
#include "TransformFeedbackObject.h"
#include "ShaderSystem.h"
#include "Shader.h"
#include "Uniform.h"
#include "Attribute.h"
#include "Camera.h"

#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/random.hpp>

#include <iostream>
#include <sstream>

#define NUM_PARTICLES 150
#define POSITION 0
#define VELOCITY 1
#define VERTEX	 2
#define NORMAL	 3
#define TEXCOORD 4
#define WIDTH 800
#define HEIGHT 600
#define SPEED 100.0f
#define UPDATE_MOD 1.0f

VBO *initCube();
void initState(VBO *&pos, VBO *&vel, bool fill);
Shader *initUpdateShader();
Shader *initRenderShader();

int main()
{
	try
	{
		sf::Window app(sf::VideoMode(WIDTH, HEIGHT, 32), "Shader Particles");

		glewExperimental = GL_TRUE;
		GLenum err = glewInit();
		if(err != GLEW_OK)
			throw T_Exception((const char *)glewGetErrorString(err));

		Camera *camera = new Camera(WIDTH, HEIGHT);

		Shader *update_shader = initUpdateShader();
		Shader *render_shader = initRenderShader();

		F32 dt = 0.016f;
		Uniform *dtu = new Uniform(GL_FLOAT, "dt", &dt);
		dtu->findLocation(update_shader->id);

		glm::mat4 mvpMat = camera->getProjectionMatrix() * camera->getViewMatrix();
		Uniform *mvp = new Uniform(GL_FLOAT_MAT4, "mvp", glm::value_ptr(mvpMat));
		mvp->findLocation(render_shader->id);

		U32 vao[4];
		GL( glGenVertexArrays(4, vao); )
		VBO *state[4];
		VBO *cube;
		TFO *tfo;

		//----------//
		// UPDATE 1 //
		//----------//
		GL( glBindVertexArray(vao[0]); )
		initState(state[0], state[1], true);
		state[0]->bind(0);
		Attribute(POSITION, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
		state[1]->bind(0);
		Attribute(VELOCITY, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
		GL( glBindVertexArray(0); )

		//----------//
		// UPDATE 2 //
		//----------//
		GL( glBindVertexArray(vao[1]); )
		initState(state[2],state[3],false);
		state[2]->bind(0);
		Attribute(POSITION, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
		state[3]->bind(0);
		Attribute(VELOCITY, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
		GL( glBindVertexArray(0); )

		U32 vertex_offset = 0;
		U32 normal_offset = 24*3*sizeof(F32) + vertex_offset;
		U32 texcoord_offset = 24*2*sizeof(F32) + normal_offset;
		//----------//
		// RENDER 1 //
		//----------//
		GL( glBindVertexArray(vao[2]); )
		cube = initCube();
		cube->bind(0);
		Attribute(VERTEX,   3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(vertex_offset));
		Attribute(NORMAL,   3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(normal_offset));
		Attribute(TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(texcoord_offset));
		state[0]->bind(0);
		Attribute(POSITION, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0), 1);
		state[1]->bind(0);
		Attribute(VELOCITY, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0), 1);
		GL( glBindVertexArray(0); )

		//----------//
		// RENDER 2 //
		//----------//
		GL( glBindVertexArray(vao[3]); )
		cube->bindIndices();
		cube->bind(0);
		Attribute(VERTEX,   3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(vertex_offset));
		Attribute(NORMAL,   3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(normal_offset));
		Attribute(TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(texcoord_offset));
		state[2]->bind(0);
		Attribute(POSITION, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0), 1);
		state[3]->bind(0);
		Attribute(VELOCITY, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0), 1);
		GL( glBindVertexArray(0); )
		
		tfo = new TFO();
		tfo->bind();
		tfo->bindBufferBase(state[2]->getId(), 0); //we'll write to the second state vbos first
		tfo->bindBufferBase(state[3]->getId(), 1); //we'll write to the second state vbos first
		tfo->unbind();
		
		GL( wglSwapIntervalEXT(0); )
		GL( glClearColor(0.01f, 0.01f, 0.01f, 1.0f); )
		GL( glEnable(GL_DEPTH_TEST); )
		GL( glDepthMask(GL_TRUE); )
		GL( glEnable(GL_CULL_FACE); )
		//glEnable(GL_BLEND);
		//glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_ONE);

		float update_mod = UPDATE_MOD;
		float stored_update_mod = 0.0f;
		float dts = 0.0f;

		sf::Mouse::SetPosition(sf::Vector2i(WIDTH/2, HEIGHT/2));
		app.ShowMouseCursor(false);
		bool ingame = true;
		bool paused = false;
		//F32 dts = 0.016f;
		U32 frame = 0;
		while(app.IsOpened())
		{
			// Get elapsed time since last loop
			dt = (F32)app.GetFrameTime()/1000.0f;
			
			if(dt > 0.001f && dt < 256.0f)
			{
				dts = dt * update_mod;
				dtu->setData(&dts);
			}

			std::stringstream out;
			out << dt;
			
			if(ingame && !paused)
				app.SetTitle(T_String("FPS: ") + out.str());
			else if(ingame && paused)
				app.SetTitle(T_String("Simulation is paued, press SPACE to resume! ") + T_String("FPS: ") + out.str());
			else if(!ingame && !paused)
				app.SetTitle(T_String("Mouse cursor is activated, press mouse button to hide mouse, SPACE to resume simulation, or ESC again to exit! ") + T_String("FPS: ") + out.str());
			else if(!ingame && paused)
				app.SetTitle(T_String("Simulation is paused and mouse cursor is activated, press mouse button to hide cursor, SPACE to resume simulation, or ESC again to exit! ") + T_String("FPS: ") + out.str());

			// First, check the applicaion events.
			sf::Event event;
			while (app.PollEvent(event))
			{
				if(ingame == true && event.Type == sf::Event::KeyPressed && event.Key.Code == sf::Keyboard::Escape)
				{
					app.ShowMouseCursor(true);
					ingame = false;
					/*if(!paused)
					{
						paused = true;
						stored_update_mod = update_mod;
						update_mod = 0.0f;
					}*/
				}
				else if(ingame == false && event.Type == sf::Event::MouseButtonPressed)
				{
					app.ShowMouseCursor(false);
					ingame = true;
					/*paused = false;
					update_mod = stored_update_mod;*/
				}
				// If application event "Closed" is received, close the application.
				else if (event.Type == sf::Event::Closed || (ingame == false && event.Type == sf::Event::KeyPressed && event.Key.Code == sf::Keyboard::Escape))
				{
					update_shader->unbind();
					render_shader->unbind();
					delete update_shader;
					delete render_shader;

					GL( glBindVertexArray(0); )
					GL( glDeleteVertexArrays(4, vao); )

					delete state[0];
					delete state[1];
					delete state[2];
					delete state[3];
					delete cube;
					delete tfo;

					delete camera;
					app.ShowMouseCursor(true);
					app.Close();
					return EXIT_SUCCESS;
				}
				// If application event "Resized" is received, call GMEngine's resize function.
				else if (event.Type == sf::Event::Resized)
				{
					GL( glViewport(0,0, event.Size.Width, event.Size.Height); )
					camera->perspective(45.0f, (F32)event.Size.Width/(F32)event.Size.Height, 1.0f, 20000.0f);
					mvpMat = camera->getProjectionMatrix() * camera->getViewMatrix();
					mvp->setData(glm::value_ptr(mvpMat));
				}
				else if(event.Type == sf::Event::KeyPressed && event.Key.Code == sf::Keyboard::O)
				{
					update_mod += 0.01f;
				}
				else if(event.Type == sf::Event::KeyPressed && event.Key.Code == sf::Keyboard::L)
				{
					update_mod -= 0.01f;
				}
				else if(paused == false && event.Type == sf::Event::KeyPressed && event.Key.Code == sf::Keyboard::Space)
				{
					stored_update_mod = update_mod;
					update_mod = 0.0f;
					paused = true;
				}
				else if(paused == true && event.Type == sf::Event::KeyPressed && event.Key.Code == sf::Keyboard::Space)
				{
					update_mod = stored_update_mod;
					paused = false;
				}
			}

			if(ingame)
			{
				sf::Vector2f mouse_pos = (sf::Vector2f)sf::Mouse::GetPosition() - sf::Vector2f(WIDTH/2, HEIGHT/2);
				sf::Mouse::SetPosition(sf::Vector2i(WIDTH/2, HEIGHT/2));

				//std::cout << "Mouse: (" << mouse_pos.x << ", " << mouse_pos.y << ") << std::endl;
			
				if(dt > 0.000001f && dt < 256.0f)
				{
					camera->rotate(mouse_pos.x*dt, glm::vec3(0.0f,1.0f,0.0f));
					camera->rotate(mouse_pos.y*dt, glm::vec3(1.0f,0.0f,0.0f));

					if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Left) || sf::Keyboard::IsKeyPressed(sf::Keyboard::A))
					{
						camera->left(dt*SPEED);
					}
					else if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Right) || sf::Keyboard::IsKeyPressed(sf::Keyboard::D))
					{
						camera->left(-dt*SPEED);
					}
					if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Up) || sf::Keyboard::IsKeyPressed(sf::Keyboard::W))
					{
						camera->forward(dt*SPEED);
					}
					else if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Down) || sf::Keyboard::IsKeyPressed(sf::Keyboard::S))
					{
						camera->forward(-dt*SPEED);
					}
					if (sf::Keyboard::IsKeyPressed(sf::Keyboard::PageUp) || sf::Keyboard::IsKeyPressed(sf::Keyboard::E))
					{
						camera->up(dt*SPEED);
					}
					else if (sf::Keyboard::IsKeyPressed(sf::Keyboard::PageDown) || sf::Keyboard::IsKeyPressed(sf::Keyboard::F))
					{
						camera->up(-dt*SPEED);
					}
			
					camera->prepare();
					mvpMat = camera->getProjectionMatrix() * camera->getViewMatrix();
					mvp->setData(glm::value_ptr(mvpMat));
				}
			}

			//----------//
			// PASS 0   //
			//----------//
			GL( glEnable(GL_RASTERIZER_DISCARD); )

			update_shader->bind();
			dtu->bind();

			tfo->bind();

			if(frame & 1)
			{
				GL( glBindVertexArray(vao[1]); )
				tfo->bindBufferBase(state[0]->getId(), 0);
				tfo->bindBufferBase(state[1]->getId(), 1);
			}
			else
			{
				GL( glBindVertexArray(vao[0]); )
				tfo->bindBufferBase(state[2]->getId(), 0);
				tfo->bindBufferBase(state[3]->getId(), 1);
			}
		
			tfo->begin(GL_POINTS);
			GL( glDrawArrays(GL_POINTS, 0, NUM_PARTICLES); )
			tfo->end();
			tfo->unbind();

			GL( glDisable(GL_RASTERIZER_DISCARD); )
			
			

			//----------//
			// PASS 1 //
			//----------//
			render_shader->bind();
			mvp->bind();
			if(frame & 1)
				GL( glBindVertexArray(vao[3]); )
			else
				GL( glBindVertexArray(vao[2]); )

			GL( glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); )
			GL( glDrawElementsInstanced(GL_TRIANGLES, 36, GL_UNSIGNED_INT, BUFFER_OFFSET(0), NUM_PARTICLES); )

			//Swap
			app.Display();
			frame++;
		}
		update_shader->unbind();
		render_shader->unbind();
		delete update_shader;
		delete render_shader;

		GL( glBindVertexArray(0); )
		GL( glDeleteVertexArrays(4, vao); )

		delete state[0];
		delete state[1];
		delete state[2];
		delete state[3];
		delete cube;
		delete tfo;
		delete camera;
		app.ShowMouseCursor(true);
		app.Close();
	}
	catch(T_Exception &e)
	{
		printf(e.what());
		system("pause");
		EXIT_FAILURE;
	}
}

VBO *initCube()
{
	U32 index_count = 36;
	U32 *indices  = new U32[index_count];
	unsigned int faces[12][3] = {{0,1,2}, {2,3,0}, {4,5,6}, {6,7,4}, {8,9,10}, {10,11,8}, 
								 {12,13,14}, {14,15,12}, {16,17,18}, {18,19,16}, {20,21,22}, {22,23,20} };
	for(unsigned int i = 0; i < 12; i++)
	{
		indices[i*3+0] = faces[i][0]; 
		indices[i*3+1] = faces[i][1]; 
		indices[i*3+2] = faces[i][2];
	}

	U32 vertex_count = 24*3;
	F32 *vertices = new F32[vertex_count];
	F32 *normals  = new F32[vertex_count];

	// Set up texture coords
	U32 tex_coords_count = 24*2;
	F32 *tex_coords = new F32[tex_coords_count];

	//
	F32 size = 8.0f;

	// Calculate average normals
	// This is important when multiple faces will share the same vertex corner
	// That is, for a cube, 3 vertices will share the same position, but for
	// different faces, thus they will not share the same face normal. What's
	// important then to achieve smooth light shading, is to calculate the
	// average normal vector, like we do here. Note below how faces will
	// share the same normal vectors for those vertices that are equal.
	//////////////////////////////////////////////////////////////////////////////////
	glm::vec3 n0 = glm::vec3(0,1,0) + glm::vec3(0,0,-1) + glm::vec3(1,0,0);
	n0 = n0 / 3.0f;
	n0 = glm::normalize(n0);

	glm::vec3 n1 = glm::vec3(0,1,0) + glm::vec3(0,0,-1) + glm::vec3(-1,0,0);
	n1 = n1 / 3.0f;
	n1 = glm::normalize(n1);

	glm::vec3 n2 = glm::vec3(0,1,0) + glm::vec3(0,0,1) + glm::vec3(-1,0,0);
	n2 = n2 / 3.0f;
	n2 = glm::normalize(n2);

	glm::vec3 n3 = glm::vec3(0,1,0) + glm::vec3(0,0,1) + glm::vec3(1,0,0);
	n3 = n3 / 3.0f;
	n3 = glm::normalize(n3);

	glm::vec3 n4 = glm::vec3(0,-1,0) + glm::vec3(0,0,1) + glm::vec3(1,0,0);
	n4 = n4 / 3.0f;
	n4 = glm::normalize(n4);

	glm::vec3 n5 = glm::vec3(0,-1,0) + glm::vec3(0,0,1) + glm::vec3(-1,0,0);
	n5 = n5 / 3.0f;
	n5 = glm::normalize(n5);

	glm::vec3 n6 = glm::vec3(0,-1,0) + glm::vec3(0,0,-1) + glm::vec3(-1,0,0);
	n6 = n6 / 3.0f;
	n6 = glm::normalize(n6);;

	glm::vec3 n7 = glm::vec3(0,-1,0) + glm::vec3(0,0,-1) + glm::vec3(1,0,0);
	n7 = n7 / 3.0f;
	n7 = glm::normalize(n7);;
	//////////////////////////////////////////////////////////////////////////////////

	// Now store the actual face data:
	// TOP
	int i = 0;
	vertices[(i+0)*3+0] =  size; vertices[(i+0)*3+1] = size; vertices[(i+0)*3+2] = -size;
	vertices[(i+1)*3+0] = -size; vertices[(i+1)*3+1] = size; vertices[(i+1)*3+2] = -size;
	vertices[(i+2)*3+0] = -size; vertices[(i+2)*3+1] = size; vertices[(i+2)*3+2] = size;
	vertices[(i+3)*3+0] =  size; vertices[(i+3)*3+1] = size; vertices[(i+3)*3+2] = size;
	normals[(i+0)*3+0] = n0.x; normals[(i+0)*3+1] = n0.y; normals[(i+0)*3+2] = n0.z;
	normals[(i+1)*3+0] = n1.x; normals[(i+1)*3+1] = n1.y; normals[(i+1)*3+2] = n1.z;
	normals[(i+2)*3+0] = n2.x; normals[(i+2)*3+1] = n2.y; normals[(i+2)*3+2] = n2.z;
	normals[(i+3)*3+0] = n3.x; normals[(i+3)*3+1] = n3.y; normals[(i+3)*3+2] = n3.z;
	tex_coords[(i+0)*2+0] = 0.0f; tex_coords[(i+0)*2+1] = 0.0f;
	tex_coords[(i+1)*2+0] = 1.0f; tex_coords[(i+1)*2+1] = 0.0f;
	tex_coords[(i+2)*2+0] = 1.0f; tex_coords[(i+2)*2+1] = 1.0f;
	tex_coords[(i+3)*2+0] = 0.0f; tex_coords[(i+3)*2+1] = 1.0f;

	// BOTTOM
	i += 4;
	vertices[(i+0)*3+0] =  size; vertices[(i+0)*3+1] = -size; vertices[(i+0)*3+2] = size;
	vertices[(i+1)*3+0] = -size; vertices[(i+1)*3+1] = -size; vertices[(i+1)*3+2] = size;
	vertices[(i+2)*3+0] = -size; vertices[(i+2)*3+1] = -size; vertices[(i+2)*3+2] = -size;
	vertices[(i+3)*3+0] =  size; vertices[(i+3)*3+1] = -size; vertices[(i+3)*3+2] = -size;
	normals[(i+0)*3+0] = n4.x; normals[(i+0)*3+1] = n4.y; normals[(i+0)*3+2] = n4.z;
	normals[(i+1)*3+0] = n5.x; normals[(i+1)*3+1] = n5.y; normals[(i+1)*3+2] = n5.z;
	normals[(i+2)*3+0] = n6.x; normals[(i+2)*3+1] = n6.y; normals[(i+2)*3+2] = n6.z;
	normals[(i+3)*3+0] = n7.x; normals[(i+3)*3+1] = n7.y; normals[(i+3)*3+2] = n7.z;
	tex_coords[(i+0)*2+0] = 0.0f; tex_coords[(i+0)*2+1] = 0.0f;
	tex_coords[(i+1)*2+0] = 1.0f; tex_coords[(i+1)*2+1] = 0.0f;
	tex_coords[(i+2)*2+0] = 1.0f; tex_coords[(i+2)*2+1] = 1.0f;
	tex_coords[(i+3)*2+0] = 0.0f; tex_coords[(i+3)*2+1] = 1.0f;

	// FRONT
	i += 4;
	vertices[(i+0)*3+0] =  size; vertices[(i+0)*3+1] =  size; vertices[(i+0)*3+2] = size;
	vertices[(i+1)*3+0] = -size; vertices[(i+1)*3+1] =  size; vertices[(i+1)*3+2] = size;
	vertices[(i+2)*3+0] = -size; vertices[(i+2)*3+1] = -size; vertices[(i+2)*3+2] = size;
	vertices[(i+3)*3+0] =  size; vertices[(i+3)*3+1] = -size; vertices[(i+3)*3+2] = size;
	normals[(i+0)*3+0] = n3.x; normals[(i+0)*3+1] = n3.y; normals[(i+0)*3+2] = n3.z;
	normals[(i+1)*3+0] = n2.x; normals[(i+1)*3+1] = n2.y; normals[(i+1)*3+2] = n2.z;
	normals[(i+2)*3+0] = n5.x; normals[(i+2)*3+1] = n5.y; normals[(i+2)*3+2] = n5.z;
	normals[(i+3)*3+0] = n4.x; normals[(i+3)*3+1] = n4.y; normals[(i+3)*3+2] = n4.z;
	tex_coords[(i+0)*2+0] = 0.0f; tex_coords[(i+0)*2+1] = 0.0f;
	tex_coords[(i+1)*2+0] = 1.0f; tex_coords[(i+1)*2+1] = 0.0f;
	tex_coords[(i+2)*2+0] = 1.0f; tex_coords[(i+2)*2+1] = 1.0f;
	tex_coords[(i+3)*2+0] = 0.0f; tex_coords[(i+3)*2+1] = 1.0f;

	// BACK
	i += 4;
	vertices[(i+0)*3+0] =  size; vertices[(i+0)*3+1] = -size; vertices[(i+0)*3+2] = -size;
	vertices[(i+1)*3+0] = -size; vertices[(i+1)*3+1] = -size; vertices[(i+1)*3+2] = -size;
	vertices[(i+2)*3+0] = -size; vertices[(i+2)*3+1] =  size; vertices[(i+2)*3+2] = -size;
	vertices[(i+3)*3+0] =  size; vertices[(i+3)*3+1] =  size; vertices[(i+3)*3+2] = -size;
	normals[(i+0)*3+0] = n7.x; normals[(i+0)*3+1] = n7.y; normals[(i+0)*3+2] = n7.z;
	normals[(i+1)*3+0] = n6.x; normals[(i+1)*3+1] = n6.y; normals[(i+1)*3+2] = n6.z;
	normals[(i+2)*3+0] = n1.x; normals[(i+2)*3+1] = n1.y; normals[(i+2)*3+2] = n1.z;
	normals[(i+3)*3+0] = n0.x; normals[(i+3)*3+1] = n0.y; normals[(i+3)*3+2] = n0.z;
	tex_coords[(i+0)*2+0] = 0.0f; tex_coords[(i+0)*2+1] = 0.0f;
	tex_coords[(i+1)*2+0] = 1.0f; tex_coords[(i+1)*2+1] = 0.0f;
	tex_coords[(i+2)*2+0] = 1.0f; tex_coords[(i+2)*2+1] = 1.0f;
	tex_coords[(i+3)*2+0] = 0.0f; tex_coords[(i+3)*2+1] = 1.0f;

	// LEFT
	i += 4;
	vertices[(i+0)*3+0] = -size; vertices[(i+0)*3+1] =  size; vertices[(i+0)*3+2] =  size;
	vertices[(i+1)*3+0] = -size; vertices[(i+1)*3+1] =  size; vertices[(i+1)*3+2] = -size;
	vertices[(i+2)*3+0] = -size; vertices[(i+2)*3+1] = -size; vertices[(i+2)*3+2] = -size;
	vertices[(i+3)*3+0] = -size; vertices[(i+3)*3+1] = -size; vertices[(i+3)*3+2] =  size;
	normals[(i+0)*3+0] = n2.x; normals[(i+0)*3+1] = n2.y; normals[(i+0)*3+2] = n2.z;
	normals[(i+1)*3+0] = n1.x; normals[(i+1)*3+1] = n1.y; normals[(i+1)*3+2] = n1.z;
	normals[(i+2)*3+0] = n6.x; normals[(i+2)*3+1] = n6.y; normals[(i+2)*3+2] = n6.z;
	normals[(i+3)*3+0] = n5.x; normals[(i+3)*3+1] = n5.y; normals[(i+3)*3+2] = n5.z;
	tex_coords[(i+0)*2+0] = 0.0f; tex_coords[(i+0)*2+1] = 0.0f;
	tex_coords[(i+1)*2+0] = 1.0f; tex_coords[(i+1)*2+1] = 0.0f;
	tex_coords[(i+2)*2+0] = 1.0f; tex_coords[(i+2)*2+1] = 1.0f;
	tex_coords[(i+3)*2+0] = 0.0f; tex_coords[(i+3)*2+1] = 1.0f;

	// RIGHT
	i += 4;
	vertices[(i+0)*3+0] = size; vertices[(i+0)*3+1] =  size; vertices[(i+0)*3+2] = -size;
	vertices[(i+1)*3+0] = size; vertices[(i+1)*3+1] =  size; vertices[(i+1)*3+2] =  size;
	vertices[(i+2)*3+0] = size; vertices[(i+2)*3+1] = -size; vertices[(i+2)*3+2] =  size;
	vertices[(i+3)*3+0] = size; vertices[(i+3)*3+1] = -size; vertices[(i+3)*3+2] = -size;
	normals[(i+0)*3+0] = n0.x; normals[(i+0)*3+1] = n0.y; normals[(i+0)*3+2] = n0.z;
	normals[(i+1)*3+0] = n3.x; normals[(i+1)*3+1] = n3.y; normals[(i+1)*3+2] = n3.z;
	normals[(i+2)*3+0] = n4.x; normals[(i+2)*3+1] = n4.y; normals[(i+2)*3+2] = n4.z;
	normals[(i+3)*3+0] = n7.x; normals[(i+3)*3+1] = n7.y; normals[(i+3)*3+2] = n7.z;
	tex_coords[(i+0)*2+0] = 0.0f; tex_coords[(i+0)*2+1] = 0.0f;
	tex_coords[(i+1)*2+0] = 1.0f; tex_coords[(i+1)*2+1] = 0.0f;
	tex_coords[(i+2)*2+0] = 1.0f; tex_coords[(i+2)*2+1] = 1.0f;
	tex_coords[(i+3)*2+0] = 0.0f; tex_coords[(i+3)*2+1] = 1.0f;
	///////////////////////////////////////////////////////////////////////
	U32 buffer_size = sizeof(F32)*vertex_count; //Vertices
	buffer_size += sizeof(F32)*vertex_count; //Normals
	//	buffer_size += sizeof(float)*params.vertex_count; //Tangents
	buffer_size += sizeof(F32)*tex_coords_count; //Texture Coordinates

	VBO *vbo = new VBO(buffer_size, index_count, indices, GL_STATIC_DRAW);
	vbo->bind(0);
	{
		vbo->buffer(GL_FLOAT, vertex_count, vertices);
		vbo->buffer(GL_FLOAT, vertex_count, normals);
		vbo->buffer(GL_FLOAT, tex_coords_count, tex_coords);
	} 
	vbo->unbind();
	delete[] indices;
	delete[] vertices;
	delete[] normals;
	delete[] tex_coords;
	return vbo;
}

Shader *initUpdateShader()
{
	ShaderSystem shaderSystem;
	Shader *program = shaderSystem.create(GL_VERTEX_SHADER, "../../shaders/physical.vs");

	GL( glBindAttribLocation(program->id, POSITION, "Position"); )
	GL( glBindAttribLocation(program->id, VELOCITY, "Velocity"); )

	const GLchar **varyings = new const GLchar*[2];
	varyings[0] = "block.Position_out";
	varyings[1] = "block.Velocity_out";
	GL( glTransformFeedbackVaryings(program->id, 2, varyings, GL_SEPARATE_ATTRIBS); )
	
	program->link();
	delete[] varyings;
	return program;
}


Shader *initRenderShader()
{
	ShaderSystem shaderSystem;
	Shader *program = shaderSystem.create(GL_VERTEX_SHADER, "../../shaders/minimal.vs", GL_FRAGMENT_SHADER, "../../shaders/minimal.fs");

	GL( glBindAttribLocation(program->id, POSITION, "Position"); )
	GL( glBindAttribLocation(program->id, VELOCITY, "Velocity"); )
	GL( glBindAttribLocation(program->id, VERTEX, "Vertex"); )
	GL( glBindAttribLocation(program->id, NORMAL, "Normal"); )
	GL( glBindAttribLocation(program->id, TEXCOORD, "TexCoord"); )
	
	program->link();
	return program;
}

void initState(VBO *&pos, VBO *&vel, bool fill)
{
	U32 vertex_count = 3*NUM_PARTICLES;
	pos = new VBO(sizeof(F32)*vertex_count, GL_DYNAMIC_COPY);
	pos->bind(0);
	{
		if(fill)
		{
			T_Vector<glm::vec3>::Type data;
			for(U32 i = 0; i < NUM_PARTICLES; i++)
				data.push_back(glm::vec3(glm::gtx::random::gaussRand1<F32>(40.0f, 9.0f), 
										 glm::gtx::random::gaussRand1<F32>(10.0f, 2.0f), 
										 glm::gtx::random::gaussRand1<F32>(40.0f, 9.0f) - 1000.0f)); //(NUM_PARTICLES - i) * -0.004f - 100.0f));
			pos->buffer(GL_FLOAT, vertex_count, &data[0]);
		}
	} 
	pos->unbind();
	
	vel = new VBO(sizeof(F32)*vertex_count, GL_DYNAMIC_COPY);
	vel->bind(0);
	{
		if(fill)
		{
			T_Vector<glm::vec3>::Type data;
			for(U32 i = 0; i < NUM_PARTICLES; i++)
				data.push_back(glm::vec3(0.0f)); //glm::gtx::random::gaussRand3<F32>(40.0f, 9.0f));
			vel->buffer(GL_FLOAT, vertex_count, &data[0]);
		}
	} 
	vel->unbind();
}
